export class CryptoUtility
{
    public static GUID():string
    {
        var d = new Date().getTime();
        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) 
        {
            var r = (d + Math.random()*16)%16 | 0;
            d = Math.floor(d/16);
            return (c=='x' ? r : (r&0x3|0x8)).toString(16);
        });
        return uuid;
    }
    public static GenRandomInteger(min:number,max:number):number
    {
        return Math.floor( Math.random() * (min - max + 1) ) + max;
    }

    public static SpliteBytes(bytes:any,startIndex:number = 0,len:number = 0,isRemoveBlock:boolean=true):any
    {
        if(startIndex>=bytes.length)return null;
        var temp:any[] = [];
        var endIndex:number = startIndex+len;
        if(endIndex>=bytes.length)endIndex = bytes.length;
        for(var i:number = startIndex;i<endIndex;i++)
            temp[i-startIndex] = bytes[i];
        if(isRemoveBlock)bytes = bytes.splice(startIndex,len);
        return temp;
    }


    //-----------------------------------------------------------------------------
    // 
    //-----------------------------------------------------------------------------
    public static GenGuidKey(GuidValue?:string):string
    {
        var uuid:string = GuidValue?GuidValue:this.GUID();
        return this.GetSHA256String(uuid)+this.GetMD5String(((GuidValue === undefined) ? uuid : GuidValue));
    }
    public static GenSNKey(SN:number):string
    {
        return this.GetMD5String(SN+"") + this.GetSHA256String(SN+"");
    }
    public static GenDynamicKey(Key1:string, Key2:string):string
    {
        var FinalKey2:string = this.GenFinalKey(Key2);
        var Key:string = Key1 + FinalKey2;
        return this.GetBase64String(this.GetSHA256Byte(Key));
    }
    public static GenFinalKey(srcData:string):string
    {
        var key = '';
        // 高低位交換
        var reverseDateTickBytes = srcData.split('');
        reverseDateTickBytes.reverse();
        // 查表ASCII表
        var queryTableSrcStr = '';
        for (var I = 0; I < reverseDateTickBytes.length; I++)
            queryTableSrcStr += reverseDateTickBytes[I].charCodeAt(0).toString(16);
        if (queryTableSrcStr.length % 2 != 0)
            queryTableSrcStr = "0" + queryTableSrcStr;
        var queryTableRetStr = '';
        for (var I = 0; I < queryTableSrcStr.length; I += 2)
        {
            var TmpStr = queryTableSrcStr[I] + queryTableSrcStr[I + 1];
            var Tmp = parseInt(TmpStr) % 127;
            queryTableRetStr += String.fromCharCode(Tmp);
        } // end for
        // 第一次 MD5 編碼
        key = this.GetBase64String(this.GetMD5Byte(queryTableRetStr));
        // 第二次 SHA256 編碼
        key += this.GetBase64String(this.GetSHA256Byte(key));
        // 第三次 SHA256 編碼
        key = this.GetBase64String(this.GetSHA256Byte(key));
        return key;
    }
    public static GetAuthToken(id:number,key?:string):string
    {   
        //todo 修改秘钥
        if(!key)key="!=~4b7#1R814@4a8";
        return this.EncodeAESString(id+"",key);
    }
    //-----------------------------------------------------------------------------
    //  AES 
    //-----------------------------------------------------------------------------
    public static EncodeAESString(data:string,key:string):string
    {
        var EData = this.GetBytes(data);
        var EIV = this.GetMD5Byte(key);
        var EncryptObj = CryptoJS.AES.encrypt(EData, EIV, { iv: EIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
        return EncryptObj.toString();
    }
    public static DecodeAESString(data:string,key:string):string
    {
        var EIV = this.GetMD5Byte(key);
        var DecryptObt = CryptoJS.AES.decrypt(data, EIV, { iv: EIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
        return this.GetString(DecryptObt);
    }
    public static EncodeAESByte(data:CryptoJS.lib.WordArray,key:string):CryptoJS.lib.WordArray
    {
        var EIV = this.GetMD5Byte(key);
        var EncryptObj = CryptoJS.AES.encrypt(data, EIV, { iv: EIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
        return EncryptObj.ciphertext;
    }
    public static DecodeAESByte(data:CryptoJS.lib.WordArray,key:string):CryptoJS.lib.WordArray
    {
        var EIV = this.GetMD5Byte(key);
        var DecryptObt = CryptoJS.AES.decrypt(this.GetBase64String(data), EIV, { iv: EIV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
        return DecryptObt;
    }
    //-----------------------------------------------------------------------------
    // GZip
    //-----------------------------------------------------------------------------
    //压缩字节
    public static CompressBytes(bytes:any,isArrayBuffer?:boolean):any
    {
        let u8Arr = isArrayBuffer?bytes:this.Word2ByteArray(bytes);
        let zipData:any = pako.gzip(u8Arr);
        return isArrayBuffer?zipData:this.Byte2WordArray(zipData);
    }
    //解压缩字节
    public static Decompress(bytes:any,isArrayBuffer?:boolean):any
    {
        let u8Arr = isArrayBuffer?bytes:this.Word2ByteArray(bytes);
        let zipData:any = pako.ungzip(u8Arr);
        return isArrayBuffer?zipData:this.Byte2WordArray(zipData);
    }
    //-----------------------------------------------------------------------------
    // WordArray <=> Uint8Array
    //-----------------------------------------------------------------------------

    public static ConvertByteArray(data:any):Uint8Array
    {
        if(Array.isArray(data))
            return new Uint8Array(data);
        return data;
    }
    public static ConvertWordArray(data:any):CryptoJS.lib.WordArray
    {
        let u8Arr = this.ConvertByteArray(data);
        return this.Byte2WordArray(u8Arr);
    }

    public static Word2ByteArray(bytes:CryptoJS.lib.WordArray,isUnshift?:boolean,...datas):Uint8Array
    {
        var words = bytes.words;
        var sigBytes = bytes.sigBytes;
        var len = sigBytes+datas.length;
        var sIndex = 0;
        // Convert
        var u8 = new Uint8Array(len);
        if(isUnshift)
        {
            sIndex = datas.length;
            for(var k = 0;k<sIndex;k++)
                u8[k]=datas[k];
        }
        for (var i = 0; i < sigBytes; i++) 
        {
            var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
            u8[i+sIndex]=byte;
        }
        if(!isUnshift)
        {
            sIndex = datas.length;
            for (var m = 0; m < sIndex; m++) 
                u8[m+sigBytes]=datas[m];
        }
        return u8;
    }
    public static Byte2WordArray(bytes:Uint8Array):CryptoJS.lib.WordArray
    {
        // Shortcut
        var len = bytes.length;
        // Convert
        var words = [];
        for (var i = 0; i < len; i++) 
            words[i >>> 2] |= (bytes[i] & 0xff) << (24 - (i % 4) * 8);
        return CryptoJS.lib.WordArray.create(words, len);
    }
    //-----------------------------------------------------------------------------
    // 
    //-----------------------------------------------------------------------------
    public static GetBytes(input:string):CryptoJS.lib.WordArray{return CryptoJS.enc.Utf8.parse(input);}
    
    public static GetString(value:CryptoJS.lib.WordArray):string{return CryptoJS.enc.Utf8.stringify(value);}

    public static GetBase64Byte(input:string):CryptoJS.lib.WordArray{return CryptoJS.enc.Base64.parse(input);}
    /**将字节数据转换成Base64字符串 */
    public static GetBase64String(value:CryptoJS.lib.WordArray):string{return CryptoJS.enc.Base64.stringify(value);}

    // public static GetCRC16Uint(value:any):number
    // {
    //     return 0;
    // }
    public static GetMD5Byte(inputString:string):CryptoJS.lib.WordArray{return CryptoJS.MD5(this.GetBytes(inputString));}
    /**将字符串转换成 MD5 加密后的字符串 */
    public static GetMD5String(inputString:string):string{return this.GetMD5Byte(inputString).toString();}
    /**将字符串转换成SHA256加密后的字节数据  */
    public static GetSHA256Byte(inputString:string):CryptoJS.lib.WordArray{return CryptoJS.SHA256(this.GetBytes(inputString));}
    /**将字符串转换成SHA256加密后的字符串 */
    public static GetSHA256String(inputString:string):string{return this.GetSHA256Byte(inputString).toString();}
}